package com.study.easyexcel.api.handler;

import com.alibaba.excel.util.ListUtils;
import com.alibaba.excel.write.handler.SheetWriteHandler;
import com.alibaba.excel.write.handler.context.SheetWriteHandlerContext;
import com.study.easyexcel.api.annotation.DropdownBox;
import com.study.easyexcel.common.exception.CheckedException;
import org.apache.poi.ss.usermodel.DataValidation;
import org.apache.poi.ss.usermodel.DataValidationConstraint;
import org.apache.poi.ss.usermodel.DataValidationHelper;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddressList;
import org.springframework.util.ObjectUtils;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.text.MessageFormat;
import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
 * 下拉框拦截器，导出数据时会根据字段上的{@link DropdownBox}生成下拉框
 *
 * @author yuhuan
 * @date 2023/5/14
 */
@SuppressWarnings({"unchecked"})
public class DropdownBoxHandler implements SheetWriteHandler {

    private final List<DropdownBox> boxes = ListUtils.newArrayListWithCapacity(16);
    private int dataSize;

    /**
     * 用户自定义的下拉框列表，结构[字段名:列表]
     */
    private Map<String, String[]> dynamicDropdownBoxList;

    protected DropdownBoxHandler() {
        dynamicDropdownBoxList = Collections.emptyMap();
    }

    public DropdownBoxHandler(Map<String, String[]> dropdownBoxList) {
        this.dynamicDropdownBoxList = dropdownBoxList;
    }

    /**
     * {@link DataValidation#setSuppressDropDownArrow(boolean)} 验证输入数据是否正确
     * {@link DataValidation#setShowErrorBox(boolean)}          输入无效值时是否显示错误框
     * {@link DataValidation#createErrorBox(String, String)}    设置无效值时的提示框内容
     */
    @Override
    public void afterSheetCreate(SheetWriteHandlerContext context) {
        Sheet sheet = context.getWriteSheetHolder().getSheet();
        DataValidationHelper helper = sheet.getDataValidationHelper();
        for (DropdownBox box : this.boxes) {
            int lastRow = this.dataSize == 0 ? box.lastRow() : (box.firstRow() + this.dataSize) - 1;
            // 创建下拉框约束
            CellRangeAddressList cellRangeList = new CellRangeAddressList(box.firstRow(), lastRow, box.firstCol(), box.lastCol());
            DataValidationConstraint constraint = helper.createExplicitListConstraint(box.explicitList());
            DataValidation dataValidation = helper.createValidation(constraint, cellRangeList);
            // 输入无效值时，是否显示错误框
            dataValidation.setShowErrorBox(box.showErrorBox());
            dataValidation.createErrorBox(box.title(), box.message());
            sheet.addValidationData(dataValidation);
        }
    }

    public static <T> DropdownBoxHandler build() {
        return new DropdownBoxHandler();
    }

    public static <T> DropdownBoxHandler buildScan(Class<T> pojoClass) {
        return build().scan(pojoClass);
    }

    /**
     * 设置下拉框的结束位置，根据起始位置和数据量计算结束位置，优先级高于{@link DropdownBox#lastRow()}
     *
     * @param dataSize 导出的数据量
     */
    public DropdownBoxHandler lastRow(Integer dataSize) {
        this.dataSize = dataSize;
        return this;
    }

    /**
     * 设置下拉框的显示列表，当{@link DropdownBox#dynamicList()}设置为{@code true}时生效
     *
     * @param explicitList 显示列表
     */
    public DropdownBoxHandler explicitList(Map<String, String[]> explicitList) {
        this.dynamicDropdownBoxList = explicitList;
        return this;
    }

    /**
     * 扫描每个字段上的下拉框编辑器
     *
     * @param pojoClass 要导出的数据
     */
    public <T> DropdownBoxHandler scan(Class<T> pojoClass) {
        Field[] declaredFields = pojoClass.getDeclaredFields();
        // 循环每个字段
        for (Field declaredField : declaredFields) {
            boolean isBind = declaredField.isAnnotationPresent(DropdownBox.class);
            if (!isBind) {
                continue;
            }
            DropdownBox dropdownBox = declaredField.getAnnotation(DropdownBox.class);
            if (!dropdownBox.dynamicList()) {
                this.boxes.add(dropdownBox);
                continue;
            }
            // 下拉框列表如果是动态属性，则给下拉框设置用户自定义的下拉框列表
            try {
                // 获取自定义的列表值
                String[] customList = dynamicDropdownBoxList.get(declaredField.getName());
                if (dynamicDropdownBoxList.size() == 0 || ObjectUtils.isEmpty(customList)) {
                    String msg = MessageFormat.format("未找到字段{0}的列表值", declaredField.getName());
                    throw new CheckedException(msg);
                }
                // 将列表值设置到注解
                InvocationHandler invocationHandler = Proxy.getInvocationHandler(dropdownBox);
                Field proxyField = invocationHandler.getClass().getDeclaredField("memberValues");
                proxyField.setAccessible(true);
                Object fieldList = proxyField.get(invocationHandler);
                Map<String, Object> fieldMap = (Map<String, Object>) fieldList;
                fieldMap.put("explicitList", customList);
            } catch (Exception e) {
                e.printStackTrace();
                throw new CheckedException("无法设置下拉框的动态列表，原因是：" + e.getMessage());
            }
            this.boxes.add(dropdownBox);
        }
        return this;
    }
}
